#include "soc_timer.h"
#include <cmath>


#ifdef HAL_TIM_MODULE_ENABLED 

static const uint32_t m_timerChannels[] = {TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4};

SOC_Timer::SOC_Timer(TimerDevId id, void *handle)
{
    InitPeripheral(id, this, handle);
}

SOC_Timer::~SOC_Timer()
{

}

void SOC_Timer::SetPerscaler(int div)
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    __HAL_TIM_SET_PRESCALER(h, div);
}

int SOC_Timer::GetPerscaler()
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    return h->Instance->PSC;
}

void SOC_Timer::SetPeriod(int val)
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    __HAL_TIM_SET_AUTORELOAD(h, val);
}

int SOC_Timer::GetPeriod()
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    return __HAL_TIM_GET_AUTORELOAD(h);
}

void SOC_Timer::SetTimerCount(uint32_t val)
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    __HAL_TIM_SetCounter(h, val);
}

uint32_t SOC_Timer::GetTimerCount()
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    return __HAL_TIM_GET_COUNTER(h);
}

void SOC_Timer::StartBase()
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    HAL_TIM_Base_Start(h);
}

void SOC_Timer::StopBase()
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    HAL_TIM_Base_Stop(h);
}

void SOC_Timer::StartBase_IT()
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    HAL_TIM_Base_Start_IT(h);    
}

void SOC_Timer::StopBase_IT()
{
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    HAL_TIM_Base_Stop_IT(h);
}

void SOC_Timer::SetElapsedCallBack(IF_SOC_Timer::ElapsedCallBack cb)
{
    m_elapsedFunc = cb;
}

void SOC_Timer::RaiseElapsed_IRQ()
{
    if(m_elapsedFunc)
    {
        m_elapsedFunc(*this);
    }
}

void SOC_Timer::StartPwm(int ch)
{
    ASSERT_ARRAY_INDEX_1(m_timerChannels, ch);

    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    HAL_TIM_PWM_Start(h, m_timerChannels[ch - 1]);
}

void SOC_Timer::StartPwmPN(int ch)
{
    ASSERT_ARRAY_INDEX_1(m_timerChannels, ch);

    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    HAL_TIM_PWM_Start(h, m_timerChannels[ch - 1]);
    HAL_TIMEx_PWMN_Start(h, m_timerChannels[ch - 1]);
}

void SOC_Timer::StopPwm(int ch)
{    
    ASSERT_ARRAY_INDEX_1(m_timerChannels, ch);

    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    HAL_TIM_PWM_Stop(h, m_timerChannels[ch - 1]);
}

void SOC_Timer::SetPwmDuty(int ch, float duty)
{
    int period = GetPeriod();
    float fv = (duty / 100.0) * period;
    int cp = round(fv);
    SetCompare(ch, cp);
}

void SOC_Timer::SetPwmDutyP(int ch, float percent)
{
    int period = GetPeriod();
    float fv = percent * period;
    int cp = round(fv);
    SetCompare(ch, cp);
}

void SOC_Timer::SetCompare(int ch, int val)
{
    ASSERT_ARRAY_INDEX_1(m_timerChannels, ch);

    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    __HAL_TIM_SET_COMPARE(h, m_timerChannels[ch - 1], val);
}

int SOC_Timer::GetCompare(int ch)
{
    ASSERT_ARRAY_INDEX_1(m_timerChannels, ch);

    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    return __HAL_TIM_GET_COMPARE(h, m_timerChannels[ch - 1]);
}

void SOC_Timer::StartOc_IT(int ch)
{
    ASSERT_ARRAY_INDEX_1(m_timerChannels, ch);

    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    HAL_TIM_OC_Start_IT(h, m_timerChannels[ch - 1]);
}

void SOC_Timer::StopOc_IT(int ch)
{
    ASSERT_ARRAY_INDEX_1(m_timerChannels, ch);

    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    HAL_TIM_OC_Stop_IT(h, m_timerChannels[ch - 1]);
}

void SOC_Timer::SetNextPoint(int ch, int us)
{
    ASSERT_ARRAY_INDEX_1(m_timerChannels, ch);
    
    auto h = GetHandleAs<TIM_HandleTypeDef>(m_devId);
    int next = (GetTimerCount() + us) % (GetPeriod() + 1);
    SetCompare(ch, next);
}

void SOC_Timer::SetOcCallBack(IF_SOC_Timer::OCCallBack cb)
{
    m_IRQOcFunc = cb;
}

void SOC_Timer::RaiseOc_IRQ(int channel)
{
    if(m_IRQOcFunc)
        m_IRQOcFunc(*this, channel);
}

#endif 
